namespace Creek.UI.PropertyGridEx
{
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing.Design;
    using System.Xml.Serialization;

    using Microsoft.VisualBasic;

    [Serializable]
    [XmlRoot("CustomProperty")]
    public class CustomProperty
    {
        #region "Private variables"

        // Common properties
        protected UICustomEventEditor.OnClick MethodDelegate;

        protected bool bIsBrowsable = false;

        protected bool bIsDropdownResizable = false;

        protected bool bIsPassword = false;

        protected bool bIsPercentage = false;

        protected bool bIsReadOnly = false;

        protected bool bParenthesize = false;

        protected bool bRef = false;

        // Filename editor properties
        protected bool bUseFileNameEditor = false;

        protected bool bVisible = true;

        // Custom choices properties

        protected BrowsableTypeConverter.LabelStyle eBrowsablePropertyLabel =
            BrowsableTypeConverter.LabelStyle.lsEllipsis;

        protected UIFilenameEditor.FileDialogType eDialogType = UIFilenameEditor.FileDialogType.LoadFileDialog;

        protected CustomChoices oChoices = null;

        // Dynamic properties

        // Extended Attributes
        [NonSerialized]
        protected AttributeCollection oCustomAttributes = null;

        // Custom Editor and Custom Type Converter
        [NonSerialized]
        protected UITypeEditor oCustomEditor = null;

        [NonSerialized]
        protected TypeConverter oCustomTypeConverter = null;

        protected object oDatasource = null;

        protected Type oDefaultType = null;

        protected object oDefaultValue = null;

        protected object oRef = null;

        protected object oSelectedItem = null;

        protected object oSelectedValue = null;

        protected object oTag = null;

        protected object oValue = null;

        protected string sCategory = "";

        protected string sDescription = "";

        protected string sDisplayMember = null;

        protected string sFilter = null;

        protected string sName = "";

        protected string sProp = "";

        protected string sValueMember = null;

        #endregion

        #region "Public methods"

        public CustomProperty()
        {
            this.sName = "New Property";
            this.oValue = new string(' ', 0);
        }

        public CustomProperty(
            string strName,
            object objValue,
            bool boolIsReadOnly,
            string strCategory,
            string strDescription,
            bool boolVisible)
        {
            this.sName = strName;
            this.oValue = objValue;
            this.bIsReadOnly = boolIsReadOnly;
            this.sDescription = strDescription;
            this.sCategory = strCategory;
            this.bVisible = boolVisible;
            if (this.oValue != null)
            {
                this.oDefaultValue = this.oValue;
            }
        }

        public CustomProperty(
            string strName,
            ref object objRef,
            string strProp,
            bool boolIsReadOnly,
            string strCategory,
            string strDescription,
            bool boolVisible)
        {
            this.sName = strName;
            this.bIsReadOnly = boolIsReadOnly;
            this.sDescription = strDescription;
            this.sCategory = strCategory;
            this.bVisible = boolVisible;
            this.bRef = true;
            this.oRef = objRef;
            this.sProp = strProp;
            if (this.Value != null)
            {
                this.oDefaultValue = this.Value;
            }
        }

        public void RebuildAttributes()
        {
            if (this.bUseFileNameEditor)
            {
                this.BuildAttributes_FilenameEditor();
            }
            else if (this.oChoices != null)
            {
                this.BuildAttributes_CustomChoices();
            }
            else if (this.oDatasource != null)
            {
                this.BuildAttributes_ListboxEditor();
            }
            else if (this.bIsBrowsable)
            {
                this.BuildAttributes_BrowsableProperty();
            }
        }

        #endregion

        #region "Private methods"

        private object DataColumn
        {
            get
            {
                var oRow = (DataRow)this.oRef;
                if (oRow.RowState != DataRowState.Deleted)
                {
                    if (this.oDatasource == null)
                    {
                        return oRow[this.sProp];
                    }
                    else
                    {
                        var oLookupTable = this.oDatasource as DataTable;
                        if (oLookupTable != null)
                        {
                            return
                                oLookupTable.Select(this.sValueMember + "=" + oRow[this.sProp])[0][this.sDisplayMember];
                        }
                        else
                        {
                            Information.Err().Raise(
                                Constants.vbObjectError + 513,
                                null,
                                "Bind of DataRow with a DataSource that is not a DataTable is not possible",
                                null,
                                null);
                            return null;
                        }
                    }
                }
                else
                {
                    return null;
                }
            }
            set
            {
                var oRow = (DataRow)this.oRef;
                if (oRow.RowState != DataRowState.Deleted)
                {
                    if (this.oDatasource == null)
                    {
                        oRow[this.sProp] = value;
                    }
                    else
                    {
                        var oLookupTable = this.oDatasource as DataTable;
                        if (oLookupTable != null)
                        {
                            if (oLookupTable.Columns[this.sDisplayMember].DataType.Equals(Type.GetType("System.String")))
                            {
                                oRow[this.sProp] =
                                    oLookupTable.Select(
                                        oLookupTable.Columns[this.sDisplayMember].ColumnName + " = \'" + value + "\'")[0
                                        ][this.sValueMember];
                            }
                            else
                            {
                                oRow[this.sProp] =
                                    oLookupTable.Select(
                                        oLookupTable.Columns[this.sDisplayMember].ColumnName + " = " + value)[0][
                                            this.sValueMember];
                            }
                        }
                        else
                        {
                            Information.Err().Raise(
                                Constants.vbObjectError + 514,
                                null,
                                "Bind of DataRow with a DataSource that is not a DataTable is impossible",
                                null,
                                null);
                        }
                    }
                }
            }
        }

        private void BuildAttributes_FilenameEditor()
        {
            var attrs = new ArrayList();
            var FilterAttribute = new UIFilenameEditor.FileDialogFilterAttribute(this.sFilter);
            var SaveDialogAttribute = new UIFilenameEditor.SaveFileAttribute();
            Attribute[] attrArray;
            attrs.Add(FilterAttribute);
            if (this.eDialogType == UIFilenameEditor.FileDialogType.SaveFileDialog)
            {
                attrs.Add(SaveDialogAttribute);
            }
            attrArray = (Attribute[])attrs.ToArray(typeof(Attribute));
            this.oCustomAttributes = new AttributeCollection(attrArray);
        }

        private void BuildAttributes_CustomChoices()
        {
            if (this.oChoices != null)
            {
                var list = new CustomChoices.CustomChoicesAttributeList(this.oChoices.Items);
                var attrs = new ArrayList();
                Attribute[] attrArray;
                attrs.Add(list);
                attrArray = (Attribute[])attrs.ToArray(typeof(Attribute));
                this.oCustomAttributes = new AttributeCollection(attrArray);
            }
        }

        private void BuildAttributes_ListboxEditor()
        {
            if (this.oDatasource != null)
            {
                var ds = new UIListboxEditor.UIListboxDatasource(ref this.oDatasource);
                var vm = new UIListboxEditor.UIListboxValueMember(this.sValueMember);
                var dm = new UIListboxEditor.UIListboxDisplayMember(this.sDisplayMember);
                UIListboxEditor.UIListboxIsDropDownResizable ddr = null;
                var attrs = new ArrayList();
                attrs.Add(ds);
                attrs.Add(vm);
                attrs.Add(dm);
                if (this.bIsDropdownResizable)
                {
                    ddr = new UIListboxEditor.UIListboxIsDropDownResizable();
                    attrs.Add(ddr);
                }
                Attribute[] attrArray;
                attrArray = (Attribute[])attrs.ToArray(typeof(Attribute));
                this.oCustomAttributes = new AttributeCollection(attrArray);
            }
        }

        private void BuildAttributes_BrowsableProperty()
        {
            var style = new BrowsableTypeConverter.BrowsableLabelStyleAttribute(this.eBrowsablePropertyLabel);
            this.oCustomAttributes = new AttributeCollection(new Attribute[] { style });
        }

        private void BuildAttributes_CustomEventProperty()
        {
            var attr = new UICustomEventEditor.DelegateAttribute(this.MethodDelegate);
            this.oCustomAttributes = new AttributeCollection(new Attribute[] { attr });
        }

        #endregion

        #region "Public properties"

        [Category("Appearance")]
        [DisplayName("Name")]
        [Description("Display Name of the CustomProperty.")]
        [ParenthesizePropertyName(true)]
        [XmlElement("Name")]
        public string Name
        {
            get
            {
                return this.sName;
            }
            set
            {
                this.sName = value;
            }
        }

        [Category("Appearance")]
        [DisplayName("ReadOnly")]
        [Description("Set read only attribute of the CustomProperty.")]
        [XmlElement("ReadOnly")]
        public bool IsReadOnly
        {
            get
            {
                return this.bIsReadOnly;
            }
            set
            {
                this.bIsReadOnly = value;
            }
        }

        [Category("Appearance")]
        [Description("Set visibility attribute of the CustomProperty.")]
        public bool Visible
        {
            get
            {
                return this.bVisible;
            }
            set
            {
                this.bVisible = value;
            }
        }

        [Category("Appearance")]
        [Description("Represent the Value of the CustomProperty.")]
        public object Value
        {
            get
            {
                if (this.bRef)
                {
                    if (this.oRef.GetType() == typeof(DataRow) || this.oRef.GetType().IsSubclassOf(typeof(DataRow)))
                    {
                        return this.DataColumn;
                    }
                    else
                    {
                        return Interaction.CallByName(this.oRef, this.sProp, CallType.Get, null);
                    }
                }
                else
                {
                    return this.oValue;
                }
            }
            set
            {
                if (this.bRef)
                {
                    if (this.oRef.GetType() == typeof(DataRow) || this.oRef.GetType().IsSubclassOf(typeof(DataRow)))
                    {
                        this.DataColumn = value;
                    }
                    else
                    {
                        Interaction.CallByName(this.oRef, this.sProp, CallType.Set, value);
                    }
                }
                else
                {
                    this.oValue = value;
                }
            }
        }

        [Category("Appearance")]
        [Description("Set description associated with the CustomProperty.")]
        public string Description
        {
            get
            {
                return this.sDescription;
            }
            set
            {
                this.sDescription = value;
            }
        }

        [Category("Appearance")]
        [Description("Set category associated with the CustomProperty.")]
        public string Category
        {
            get
            {
                return this.sCategory;
            }
            set
            {
                this.sCategory = value;
            }
        }

        [XmlIgnore]
        public Type Type
        {
            get
            {
                if (this.Value != null)
                {
                    return this.Value.GetType();
                }
                else
                {
                    if (this.oDefaultValue != null)
                    {
                        return this.oDefaultValue.GetType();
                    }
                    else
                    {
                        return this.oDefaultType;
                    }
                }
            }
        }

        [XmlIgnore]
        public AttributeCollection Attributes
        {
            get
            {
                return this.oCustomAttributes;
            }
            set
            {
                this.oCustomAttributes = value;
            }
        }

        [Category("Behavior")]
        [Description("Indicates if the property is browsable or not.")]
        [XmlElement(IsNullable = false)]
        public bool IsBrowsable
        {
            get
            {
                return this.bIsBrowsable;
            }
            set
            {
                this.bIsBrowsable = value;
                if (value)
                {
                    this.BuildAttributes_BrowsableProperty();
                }
            }
        }

        [Category("Appearance")]
        [DisplayName("Parenthesize")]
        [Description(
            "Indicates whether the name of the associated property is displayed with parentheses in the Properties window."
            )]
        [DefaultValue(false)]
        [XmlElement("Parenthesize")]
        public bool Parenthesize
        {
            get
            {
                return this.bParenthesize;
            }
            set
            {
                this.bParenthesize = value;
            }
        }

        [Category("Behavior")]
        [Description("Indicates the style of the label when a property is browsable.")]
        [XmlElement(IsNullable = false)]
        public BrowsableTypeConverter.LabelStyle BrowsableLabelStyle
        {
            get
            {
                return this.eBrowsablePropertyLabel;
            }
            set
            {
                bool Update = false;
                if (value != this.eBrowsablePropertyLabel)
                {
                    Update = true;
                }
                this.eBrowsablePropertyLabel = value;
                if (Update)
                {
                    var style = new BrowsableTypeConverter.BrowsableLabelStyleAttribute(value);
                    this.oCustomAttributes = new AttributeCollection(new Attribute[] { style });
                }
            }
        }

        [Category("Behavior")]
        [Description("Indicates if the property is masked or not.")]
        [XmlElement(IsNullable = false)]
        public bool IsPassword
        {
            get
            {
                return this.bIsPassword;
            }
            set
            {
                this.bIsPassword = value;
            }
        }

        [Category("Behavior")]
        [Description("Indicates if the property represents a value in percentage.")]
        [XmlElement(IsNullable = false)]
        public bool IsPercentage
        {
            get
            {
                return this.bIsPercentage;
            }
            set
            {
                this.bIsPercentage = value;
            }
        }

        [Category("Behavior")]
        [Description("Indicates if the property uses a FileNameEditor converter.")]
        [XmlElement(IsNullable = false)]
        public bool UseFileNameEditor
        {
            get
            {
                return this.bUseFileNameEditor;
            }
            set
            {
                this.bUseFileNameEditor = value;
            }
        }

        [Category("Behavior")]
        [Description("Apply a filter to FileNameEditor converter.")]
        [XmlElement(IsNullable = false)]
        public string FileNameFilter
        {
            get
            {
                return this.sFilter;
            }
            set
            {
                bool UpdateAttributes = false;
                if (value != this.sFilter)
                {
                    UpdateAttributes = true;
                }
                this.sFilter = value;
                if (UpdateAttributes)
                {
                    this.BuildAttributes_FilenameEditor();
                }
            }
        }

        [Category("Behavior")]
        [Description("DialogType of the FileNameEditor.")]
        [XmlElement(IsNullable = false)]
        public UIFilenameEditor.FileDialogType FileNameDialogType
        {
            get
            {
                return this.eDialogType;
            }
            set
            {
                bool UpdateAttributes = false;
                if (value != this.eDialogType)
                {
                    UpdateAttributes = true;
                }
                this.eDialogType = value;
                if (UpdateAttributes)
                {
                    this.BuildAttributes_FilenameEditor();
                }
            }
        }

        [Category("Behavior")]
        [Description("Custom Choices list.")]
        [XmlIgnore]
        public CustomChoices Choices
        {
            get
            {
                return this.oChoices;
            }
            set
            {
                this.oChoices = value;
                this.BuildAttributes_CustomChoices();
            }
        }

        [Category("Databinding")]
        [XmlIgnore]
        public object Datasource
        {
            get
            {
                return this.oDatasource;
            }
            set
            {
                this.oDatasource = value;
                this.BuildAttributes_ListboxEditor();
            }
        }

        [Category("Databinding")]
        [XmlElement(IsNullable = false)]
        public string ValueMember
        {
            get
            {
                return this.sValueMember;
            }
            set
            {
                this.sValueMember = value;
                this.BuildAttributes_ListboxEditor();
            }
        }

        [Category("Databinding")]
        [XmlElement(IsNullable = false)]
        public string DisplayMember
        {
            get
            {
                return this.sDisplayMember;
            }
            set
            {
                this.sDisplayMember = value;
                this.BuildAttributes_ListboxEditor();
            }
        }

        [Category("Databinding")]
        [XmlElement(IsNullable = false)]
        public object SelectedValue
        {
            get
            {
                return this.oSelectedValue;
            }
            set
            {
                this.oSelectedValue = value;
            }
        }

        [Category("Databinding")]
        [XmlElement(IsNullable = false)]
        public object SelectedItem
        {
            get
            {
                return this.oSelectedItem;
            }
            set
            {
                this.oSelectedItem = value;
            }
        }

        [Category("Databinding")]
        [XmlElement(IsNullable = false)]
        public bool IsDropdownResizable
        {
            get
            {
                return this.bIsDropdownResizable;
            }
            set
            {
                this.bIsDropdownResizable = value;
                this.BuildAttributes_ListboxEditor();
            }
        }

        [XmlIgnore]
        public UITypeEditor CustomEditor
        {
            get
            {
                return this.oCustomEditor;
            }
            set
            {
                this.oCustomEditor = value;
            }
        }

        [XmlIgnore]
        public TypeConverter CustomTypeConverter
        {
            get
            {
                return this.oCustomTypeConverter;
            }
            set
            {
                this.oCustomTypeConverter = value;
            }
        }

        [XmlIgnore]
        public object Tag
        {
            get
            {
                return this.oTag;
            }
            set
            {
                this.oTag = value;
            }
        }

        [XmlIgnore]
        public object DefaultValue
        {
            get
            {
                return this.oDefaultValue;
            }
            set
            {
                this.oDefaultValue = value;
            }
        }

        [XmlIgnore]
        public Type DefaultType
        {
            get
            {
                return this.oDefaultType;
            }
            set
            {
                this.oDefaultType = value;
            }
        }

        [XmlIgnore]
        public UICustomEventEditor.OnClick OnClick
        {
            get
            {
                return this.MethodDelegate;
            }
            set
            {
                this.MethodDelegate = value;
                this.BuildAttributes_CustomEventProperty();
            }
        }

        #endregion

        #region "CustomPropertyDescriptor"

        public class CustomPropertyDescriptor : PropertyDescriptor
        {
            protected CustomProperty oCustomProperty;

            public CustomPropertyDescriptor(CustomProperty myProperty, Attribute[] attrs)
                : base(myProperty.Name, attrs)
            {
                if (myProperty == null)
                {
                    this.oCustomProperty = null;
                }
                else
                {
                    this.oCustomProperty = myProperty;
                }
            }

            public override Type ComponentType
            {
                get
                {
                    return this.GetType();
                }
            }

            public override bool IsReadOnly
            {
                get
                {
                    return this.oCustomProperty.IsReadOnly;
                }
            }

            public override Type PropertyType
            {
                get
                {
                    return this.oCustomProperty.Type;
                }
            }

            public override string Description
            {
                get
                {
                    return this.oCustomProperty.Description;
                }
            }

            public override string Category
            {
                get
                {
                    return this.oCustomProperty.Category;
                }
            }

            public override string DisplayName
            {
                get
                {
                    return this.oCustomProperty.Name;
                }
            }

            public override bool IsBrowsable
            {
                get
                {
                    return this.oCustomProperty.IsBrowsable;
                }
            }

            public object CustomProperty
            {
                get
                {
                    return this.oCustomProperty;
                }
            }

            public override bool CanResetValue(object component)
            {
                if ((this.oCustomProperty.DefaultValue != null) || (this.oCustomProperty.DefaultType != null))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

            public override object GetValue(object component)
            {
                return this.oCustomProperty.Value;
            }

            public override void ResetValue(object component)
            {
                this.oCustomProperty.Value = this.oCustomProperty.DefaultValue;
                this.OnValueChanged(component, EventArgs.Empty);
            }

            public override void SetValue(object component, object value)
            {
                this.oCustomProperty.Value = value;
                this.OnValueChanged(component, EventArgs.Empty);
            }

            public override bool ShouldSerializeValue(object component)
            {
                object oValue = this.oCustomProperty.Value;
                if ((this.oCustomProperty.DefaultValue != null) && (oValue != null))
                {
                    return ! oValue.Equals(this.oCustomProperty.DefaultValue);
                }
                else
                {
                    return false;
                }
            }
        }

        #endregion
    }
}